# Author: Damian 'Rush' Kaczmarek
# License: MIT

package SetWords;
use base Module;
use strict;
use integer;    # we do not want floating point operations in here!
use DBD::SQLite;
use Auth;
use String::Approx qw(amatch);
use Format;

my $dbh;

my $hintfuzziness = Config::get("hintfuzziness", "20%");
my $hintlimit     = Config::get("hintlimit",     "10");

sub on_load
{
	$dbh = DBI->connect("dbi:SQLite:SetWords.db") || die "Cannot open database: $DBI::errstr";
	$dbh->func(
		'regexp', 2,
		sub {
			my ($fuzzy, $string) = @_;

			if ($fuzzy eq $string) {
				return $string;
			}

			return amatch($fuzzy, ["i " . $hintfuzziness], $string)
			  ;    # Incasesensitive matching with percantage of fuzziness
		},
		'create_function'
	);

	$dbh->do("CREATE TABLE IF NOT EXISTS setwords (word, definition, whoset, dateset INTEGER)");
}

sub on_unload
{
	$dbh->disconnect();
	$dbh = undef;
}

sub get_wordfuzzy
{
	my $word = $_[0];

	my $sel = $dbh->prepare("SELECT word FROM setwords WHERE word REGEXP (?);");

	my @res = $dbh->selectall_arrayref($sel, undef, $word);
	my @hits;

	if ($res[0][0]) {
		my $limit = $hintlimit;
		if (@{$res[0]} < $hintlimit) {
			$limit = @{$res[0]};
		}
		foreach my $elem (@{$res[0]}[0 .. ($limit - 1)]) {
			push(@hits, @{$elem}[0]);
		}
		return @hits;
	}
}

sub get_word
{
	my $word = $_[0];

	my @res = $dbh->selectall_arrayref(
		"SELECT definition,whoset,dateset FROM setwords WHERE word = " . $dbh->quote($word) . ";");
	if ($res[0][0]) {
		return @{$res[0][0]};
	}
}

sub set_word
{
	my $word   = $_[0];
	my $def    = $_[1];
	my $whoset = $_[2];

	if (!get_word($word)) {
		$dbh->do(   "INSERT INTO setwords VALUES("
			  . $dbh->quote($word) . ", "
			  . $dbh->quote($def) . ", "
			  . $dbh->quote($whoset) . ", "
			  . time()
			  . ")");
	}
	else {
		Core::send_msg("Przykro mi, definicja dla '" . $word . "' już istnieje.\n");
	}
}

sub unset_word
{
	my $word = $_[0];

	$dbh->do("DELETE FROM setwords WHERE word = " . $dbh->quote($word));
}

sub on_public
{
	my $text = $_[1];

	if ($text =~ /^!set/) {
		$text =~ s/^!set //;    # Extract

		my $idx = index($text, ' ');
		my $word = substr($text, 0, $idx);
		my $def = substr($text, $idx + 1);

		set_word($word, $def, $_[0]);
	}
	elsif ($text =~ /^!say/) {
		$text =~ s/^!say //;

		my $result = power_parse($_[0], $text);
		if ($result) {
			Core::send_msg($result);
		}
		else {
			Core::send_msg($_[0] . ": Nie przekazano odpowiednich argumentów...\n");
		}
	}
	elsif ($text =~ /^!testformat/) {
		$text =~ s/^!testformat //;
		Core::send_msg("Middle: " . Format::fromIRC($text));
		Core::send_msg("IRC:    " . Format::toIRC(Format::fromIRC($text)));
	}
	elsif ($text =~ /^!format/) {
		$text =~ s/^!format //;
		Core::send_msg("Middle: " . Format::toIRC($text));
	}
	elsif ($text =~ /^\?\!(\S+)/) {    # Getting a not-parsed version of definition
		my $word = $1;
		my @res  = get_word($word);
		if (@res > 1) {
			Core::send_msg($word . " = " . $res[0]);
		}
		else {
			Core::send_msg("Definicja dla '" . $word . "' nie została znaleziona.");
		}
	}
	elsif ($text =~ /^\?\S/) {
		$text = substr($text, 1);
		my @args = split(/ /, $text);
		my $word = shift(@args);

		my @res = get_word($word);
		if (@res > 1) {
			my $def = power_parse($_[0], $res[0], @args);
			if ($def) {
				Core::send_msg($word . "? " . $def);
			}
			else {
				Core::send_msg($word . " == ORLY?");
			}
		}
		else {
			my @hits = get_wordfuzzy($word);
			if (@hits > 1) {
				Core::send_msg("Hmm, a może coś z tego?: " . join(', ', @hits));
			}
			elsif ($hits[0]) {
				Core::send_msg("Czy chodziło Ci o: '" . $hits[0] . "'?");
			}
			else {
				Core::send_msg("Definicja dla '" . $word . "' nie została znaleziona.");
			}
		}
	}
	elsif ($text =~ /^$Core::nickname(:|,) (\S.*)/) {
		$text = $2;
		my @args = split(/ /, $text);
		my $word = shift(@args);

		my @res = get_word($word);
		if (@res > 1) {
			my $def = power_parse($_[0], $res[0], @args);
			if ($def) {
				Core::send_msg($word . "? " . $def);
			}
			else {
				Core::send_msg($word . " == ORLY?");
			}
		}
		else {
			Core::send_msg("Definicja dla '" . $word . "' nie została znaleziona.");
		}
	}
	elsif ($text =~ /^!unset/) {
		$text =~ s/^!unset //;    # Extract
		my $idx = index($text, ' ');
		my $word;
		if ($idx == -1) {
			$word = $text;
		}
		else {
			$word = substr($text, 0, $idx);
		}
		Auth::run_authed($_[0], \&unset_word, $word);
	}
	elsif ($text =~ /^!whoset/) {
		$text =~ s/^!whoset //;    # Extract
		my $idx = index($text, ' ');
		my $word;
		if ($idx == -1) {
			$word = $text;
		}
		else {
			$word = substr($text, 0, $idx);
		}
		my @res = get_word($word);
		if (@res > 1) {
			Core::send_msg($_[0] . ": '" 
				  . $word
				  . "' ustawiono przez '"
				  . $res[1] . "'("
				  . Core::howlongago($res[2])
				  . ")");
		}
		else {
			Core::send_msg("Definicja dla '" . $word . "' nie znaleziona.");
		}
	}
}

my %meta_func = (
	nick      => \&meta_nick,
	selfnick  => \&self_nick,
	nselfnick => \&nself_nick,
	botnick   => \&bot_nick,
);

sub meta_nick
{
	my @nicks = Core::get_names();
	my $nick;
	do {
		$nick = $nicks[int(rand(@nicks))];
	} while ($nick eq Core::nickname);

	return $nick;
}

sub self_nick
{
	return $_[0];
}

sub bot_nick
{
	return $Core::nickname;
}

sub nself_nick
{
	my $nick;
	do {
		$nick = meta_nick();
	} while ($nick eq $_[0]);
	return $nick;
}

sub getmeta
{
	my $selfnick = shift;
	my $meta = substr($_[0], 1, -1);    # chop the beginning and trailing %
	shift;
	my @randmeta = split(/\|/, $meta);
	if (@randmeta > $meta) {
		$meta = $randmeta[int(rand(@randmeta))];
	}
	if (exists($meta_func{$meta})) {
		$meta = $meta_func{$meta}->($selfnick, @_);
	}
	elsif ($meta eq 'arg') {
		print $_[0] . " " . $_[1];
		$meta = $_[0];
	}
	elsif ($meta =~ /^arg[0-9]/) {
		$meta = @_[substr($meta, 3, 4)];
	}
	return $meta;
}

sub power_parse
{
	my $selfnick = shift;
	my $in       = $_[0];
	shift;
	my $out;
	while ($in) {
		my $idx = index($in, '%');
		my $idx2;
		if ($idx != -1) {
			$out = $out . substr($in, 0, $idx);
			$in = substr($in, $idx);
			$idx2 = index($in, '%', 1);

			my $meta = getmeta($selfnick, substr(($in, 0, $idx2 + 1)), @_);
			if (!$meta) {
				return;
			}
			$out .= $meta;
			$in = substr($in, $idx2 + 1);
		}
		else {
			$out .= $in;
			$in = undef;
		}
	}
	return $out;
}
1;
